package com.bnq.logc

import android.content.Context
import com.bnq.logc.action.SendAction
import com.bnq.logc.config.LogcConfig
import com.bnq.logc.data.CheckUploadResult
import com.bnq.logc.db.LogcDao
import com.bnq.logc.db.tmp.LogcTmpDao
import com.bnq.logc.logger.Logger
import com.bnq.logc.logger.PrinterLogger
import com.bnq.logc.logger.WriteLogger
import com.bnq.logc.tmp.TmpFileUploadManager
import com.bnq.logc.tmp.TmpUploadFile
import com.bnq.logc.tmp.TmpUploadFileImpl
import com.bnq.logc.utils.CollectionUtil
import com.bnq.logc.work.LogcUploadManager
import com.bnq.logc.work.ScheduledUploadManager
import java.io.File

object LogcManager {

    private lateinit var dispatcher: LogcDispatcher

    private val loggers = mutableListOf<Logger>()//PrinterLogger(),WriteLogger()

    private var config: LogcConfig? = null

    private val mTmpUploadFile: TmpUploadFile by lazy { TmpUploadFileImpl() }

    fun init(config: LogcConfig){
        this.config = config

        dispatcher = LogcDispatcher()

        config.loggers?.let {
            loggers.addAll(it)
        }
        if(config.mIsPrintLog){
            loggers.add(PrinterLogger())
        }
        loggers.add(WriteLogger(dispatcher))

        initUpload(config.mContent,config)
    }

    private fun initUpload(context: Context,config: LogcConfig){
        LogcDao.init(context)

        val scheduleConfig = config.mUploadScheduleConfig
        val scheduleUpload = ScheduledUploadManager.getScheduledUpload(context,scheduleConfig)
        if(config.mIsUploadLog){
            scheduleUpload.schedule(scheduleConfig.time,scheduleConfig.unit)
        }else{
            scheduleUpload.cancel()
        }

        //检测删除本地log文件，会先同步上传之后再删除
        dispatcher.dispatchDelete()

        LogcTmpDao.init(context)
        //上传之前2/20 - 3/29号之间记录的简单日志
        if(config.mIsOpenSimpleCacheLogUpload){
            dispatcher.dispatchUploadSimpleCacheLog()
        }

        //初始化Tmp文件上传
        TmpFileUploadManager.init(config.mTmpScheduleUploadFileConfig)
    }

    fun getConfig():LogcConfig{
        if(config == null) throw java.lang.RuntimeException("Logc config is null")
        return config!!
    }

    fun log(level:Int, tag: String, log: String){
        _log(level, tag, log)
        if(level == LogcLevel.ERROR || level == LogcLevel.SOS){
            _send(level,tag,log, config?.mDefaultSendBodyParamFun?.invoke())
        }
    }

    private fun _log(level:Int, tag: String, log: String){
        loggers.forEach { logger ->
            if(logger.isEnabled()){
                logger.log(level,tag,log)
            }
        }
    }

    fun send(level:Int, tag: String, log: String,map:Map<String,Any?>?,isUseDefaultParam:Boolean = true){
        _log(level, tag, log)
        _send(level,tag, log,
            (if(isUseDefaultParam) CollectionUtil.mergeMap(config?.mDefaultSendBodyParamFun?.invoke(),map) else map) as Map<String, Any?>?
        )
    }

    fun _send(level:Int, tag: String, log: String,map:Map<String,Any?>?){
        if(getConfig().mIsSendLog){
            dispatcher.dispatchSend(
                SendAction(level,tag,log,System.currentTimeMillis(),
                    "${Thread.currentThread().name}:${Thread.currentThread().id}",
                    false,map)
            )
        }
    }

    fun sendOnly(level:Int, tag: String, log: String,){
        _send(level,tag, log,
            (CollectionUtil.mergeMap(config?.mDefaultSendBodyParamFun?.invoke(), null)) as Map<String, Any?>?
        )
    }

    fun syncCurrentLog(){
        dispatcher.dispatchUploadOne()
    }

    fun syncLogFileNow(count:Int = 1,callback:((CheckUploadResult)->Unit)? = null){
        dispatcher.dispatchUploadCountNow(count, callback)
    }

    fun syncLogFileNow(files:List<File>, callback:((CheckUploadResult)->Unit)? = null){
        dispatcher.dispatchFileNow(files, callback)
    }

    fun syncTmpFileNow(files:List<File>, cloudPathNames:List<String>, callback:((CheckUploadResult)->Unit)? = null){
        dispatcher.dispatchTmpFileNow(files, cloudPathNames, callback)
    }

    fun getTmpUploadFile():TmpUploadFile {
        return mTmpUploadFile
    }
}